/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cj_want_ffi.h"

#include <cstring>
#include <string>
#include <vector>

#include "cj_utils_ffi.h"
#include "element_name.h"
#include "want.h"
// #include "want_params_wrapper.h"
// #include "hilog_tag_wrapper.h"
#include "hilog.h"
#include "nlohmann/json.hpp"
using json = nlohmann::json;

using Keels::AAFwk::Want;
using Keels::AppExecFwk::ElementName;

// Attention: The function does not handle entities.
WantHandle FFICJWantCreateWithWantInfo(CJWantParams params)
{
    Want* want = new (std::nothrow) Want();
    if (want == nullptr) {
        // TAG_LOGE(AAFwkTag::DEFAULT, "null want");
        HILOG_ERROR("null want");
        return nullptr;
    }

    auto element = reinterpret_cast<ElementName*>(params.elementName);
    want->SetBundleName(element->GetBundleName());
    want->SetModuleName(element->GetModuleName());
    want->SetAbilityName(element->GetAbilityName());
    want->ParseJson(ConvertFromWantParamsFormat(params.parameters));
    // want->SetElement(*element);
    // want->SetFlags(params.flags);
    // want->SetUri(params.uri);
    // want->SetAction(params.action);
    // want->SetType(params.wantType);
    // want->SetParams(Keels::AAFwk::WantParamWrapper::ParseWantParamsWithBrackets(params.parameters));

    return want;
}

std::string ConvertFromWantParamsFormat(const std::string& input)
{
    auto root = json::parse(input);

    if (root.is_null() || !root.is_object()) {
        HILOG_ERROR("Invalid input format");
        return "{\"params\":[]}";
    }

    json result = json::object();
    json paramsArray = json::array();

    for (auto it = root.begin(); it != root.end(); ++it) {
        std::string key = it.key();
        auto valueObj = it.value();

        if (!valueObj.is_object()) {
            HILOG_ERROR("Invalid value format for key: %{public}s", key.c_str());
            continue;
        }

        if (valueObj.empty()) {
            HILOG_ERROR("Empty object for key: %{public}s", key.c_str());
            continue;
        }

        auto typeIt = valueObj.begin();
        std::string typeStr = typeIt.key();
        std::string value = typeIt.value().get<std::string>();

        int originalType = std::stoi(typeStr);
        if (originalType >= 6) {
            originalType += 1;
        }

        json paramItem = { { "key", key }, { "type", originalType }, { "value", value } };

        paramsArray.push_back(paramItem);
    }

    result["params"] = paramsArray;
    return result.dump();
}

void FFICJWantDelete(WantHandle want)
{
    if (want == nullptr) {
        // TAG_LOGE(AAFwkTag::DEFAULT, "null want");
        HILOG_ERROR("null want");
        return;
    }
    auto actualWant = reinterpret_cast<Want*>(want);
    delete actualWant;
    actualWant = nullptr;
}

CJWantParams* FFICJWantGetWantInfo(WantHandle want)
{
    CJWantParams* buffer = static_cast<CJWantParams*>(malloc(sizeof(CJWantParams)));
    if (buffer == nullptr) {
        // TAG_LOGE(AAFwkTag::DEFAULT, "null buffer");
        HILOG_ERROR("null buffer");
        return nullptr;
    }

    auto actualWant = reinterpret_cast<Want*>(want);
    // auto element = actualWant->GetElement();
    // ElementNameHandle elementName = new ElementName(
    //     element.GetDeviceID(), element.GetBundleName(), element.GetAbilityName(), element.GetModuleName());
    ElementNameHandle elementName =
        new ElementName("", actualWant->GetBundleName(), actualWant->GetAbilityName(), actualWant->GetModuleName());
    if (elementName == nullptr) {
        free(buffer);
        // TAG_LOGE(AAFwkTag::DEFAULT, "element name null");
        HILOG_ERROR("element name null");
        return nullptr;
    }
    buffer->elementName = elementName;
    buffer->flags = true;
    buffer->uri = nullptr;
    buffer->action = nullptr;
    buffer->wantType = nullptr;
    buffer->entities = nullptr; // reference vector<String>
    buffer->parameters = CreateCStringFromString(ConvertToWantParamsFormat(actualWant->ToJson()));
    HILOG_INFO("buffer->parameters: %{public}s", buffer->parameters);
    // buffer->flags = actualWant->GetFlags();
    // buffer->uri = CreateCStringFromString(actualWant->GetUriString());
    // buffer->action = CreateCStringFromString(actualWant->GetAction());
    // buffer->wantType = CreateCStringFromString(actualWant->GetType());
    // buffer->entities = const_cast<std::vector<std::string>*>(&(actualWant->GetEntities()));
    // buffer->parameters = CreateCStringFromString(Keels::AAFwk::WantParamWrapper(actualWant->GetParams()).ToString());
    return buffer;
}

std::string ConvertToWantParamsFormat(const std::string& input)
{
    auto root = json::parse(input);

    if (!root.contains("params") || !root["params"].is_array()) {
        HILOG_ERROR("Invalid input format");
        return "{}";
    }

    json result = json::object();

    for (const auto& item : root["params"]) {
        if (!item.contains("key") || !item.contains("type") || !item.contains("value")) {
            HILOG_ERROR("Invalid item format: missing key, type or value");
            return "{}";
        }

        std::string key = item["key"].get<std::string>();
        int typeVal = item["type"].get<int>();
        std::string value = item["value"].get<std::string>();

        json typeObj;
        typeObj[typeVal >= 7 ? std::to_string(typeVal - 1) : std::to_string(typeVal)] = value;

        result[key] = typeObj;
    }

    return result.dump();
}

void FFICJWantParamsDelete(CJWantParams* params)
{
    if (params == nullptr) {
        // TAG_LOGE(AAFwkTag::DEFAULT, "argc null");
        HILOG_ERROR("argc null");
        return;
    }
    auto actualElementName = reinterpret_cast<ElementName*>(params->elementName);
    delete actualElementName;
    actualElementName = nullptr;

    free(static_cast<void*>(params->uri));
    free(static_cast<void*>(params->action));
    free(static_cast<void*>(params->wantType));
    free(static_cast<void*>(params->parameters));
    // Entities are reference, do not free.
    free(static_cast<void*>(params));
}

void FFICJWantAddEntity(WantHandle want, const char* entity)
{
    if (want == nullptr || entity == nullptr) {
        // TAG_LOGE(AAFwkTag::DEFAULT, "Want or entity null");
        HILOG_ERROR("Want or entity null");
        return;
    }
    // auto actualWant = reinterpret_cast<Want*>(want);
    // actualWant->AddEntity(entity);
}

// WantHandle FFICJWantParseUri(const char* uri)
// {
//     if (uri == nullptr) {
//         TAG_LOGE(AAFwkTag::DEFAULT, "Uri null");
//         return nullptr;
//     }
//     return Want::ParseUri(uri);
// }